package dfferber.xstream;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import com.thoughtworks.xstream.mapper.Mapper;
import com.thoughtworks.xstream.mapper.MapperWrapper;

public class SanitizedClassMapper extends MapperWrapper {

	Map<Class<?>, Class<?>> classToSuperClass = new HashMap<Class<?>, Class<?>>();
	Set<Class<?>> ignoreClasses = new HashSet<Class<?>>();
	Map<Class<?>, Class<?>> superClasses = new HashMap<Class<?>, Class<?>>();
	
	public SanitizedClassMapper(Mapper wrapped) {
		super(wrapped);
	}
	
	protected Class<?> resolveSuperClass(Class<?> type) {
		if (ignoreClasses.contains(type)) return null;
		Class<?> topClass = classToSuperClass.get(type);
		if (topClass != null) return topClass;
		Iterator<Entry<Class<?>, Class<?>>>	iterator = superClasses.entrySet().iterator(); 
		while (iterator.hasNext()) {
			Entry<Class<?>, Class<?>> entry = iterator.next();
			Class<?> superClass = entry.getKey();
			if (superClass.isAssignableFrom(type)) {
				Class<?> otherClass = entry.getValue();
				classToSuperClass.put(type, otherClass);
				return otherClass;
			}
		}
		ignoreClasses.add(type);
		return null;
	}

	@Override
	public Class defaultImplementationOf(Class type) {
		Class<?> superClass = resolveSuperClass(type);
		if (superClass == null) {
			return super.defaultImplementationOf(type);
		} else if (type.equals(superClass)) {
			return super.defaultImplementationOf(superClass);
		} else {
			return this.defaultImplementationOf(superClass);
		}
	}

	@Override
	public boolean isImmutableValueType(Class type) {
		Class<?> superClass = resolveSuperClass(type);
		if (superClass == null) {
			return super.isImmutableValueType(type);
		} else if (type.equals(superClass)) {
			return super.isImmutableValueType(superClass);
		} else {
			return this.isImmutableValueType(superClass);
		}
	}
	
	@Override
	public String serializedClass(Class type) {
		Class<?> superClass = resolveSuperClass(type);
		if (superClass == null) {
			return super.serializedClass(type);
		} else if (type.equals(superClass)) {
			return super.serializedClass(superClass);
		} else {
			return this.serializedClass(superClass);
		}
	}
	
	@Override
	public boolean shouldSerializeMember(Class definedIn, String fieldName) {
		Class<?> superClass = resolveSuperClass(definedIn);
		if (superClass == null) {
			return super.shouldSerializeMember(definedIn, fieldName);
		} else if (superClass.equals(definedIn)) {
			return super.shouldSerializeMember(superClass, fieldName);
		} else {
			return false;
		}
	}
	
	public void setIgnoreClasses(Class<?> ignoreClass) {
		this.ignoreClasses.add(ignoreClass);
	}

	public void setSuperClass(Class<?> superClass) {
		this.superClasses.put(superClass, superClass);
	}

	public void exchangeSuperClass(Class<?> superClass, Class<?> otherClass) {
		this.superClasses.put(superClass, otherClass);
	}

}
